package com.cqs.learning.spring.cloud.producer.service;

import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.cqs.learning.spring.cloud.producer.config.Contants;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.TransactionResult;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * redis sentinel rules 处理类
 *
 * @author lixw
 * @date created in 23:57 2020/4/7
 */
@Service
public class RedisSentinelRulesService {

//    @Resource(name = "redisConnectionConfig")
    private RedisConnectionConfig redisConnectionConfig;


    /**
     * 更新限流规则接口
     *
     * @param rules
     */
    public TransactionResult pushFlowRules(String rules) {
        return pushRules(rules, Contants.SENTINEL_FLOW_RULES_CHANNEL_KEY, Contants.SENTINEL_FLOW_RULES_CHANNEL);
    }

    /**
     * 更新熔断规则接口
     *
     * @param rules
     */
    public TransactionResult pushDegradeRules(String rules) {
        return pushRules(rules, Contants.SENTINEL_DEGRADE_RULES_CHANNEL_KEY, Contants.SENTINEL_DEGRADE_RULES_CHANNEL);
    }

    public TransactionResult pushRules(String rules, String key, String channel) {
        StatefulRedisPubSubConnection<String, String> connection = getRedisClient(redisConnectionConfig).connectPubSub();
        RedisPubSubCommands<String, String> subCommands = connection.sync();
        subCommands.multi();
        subCommands.set(key, rules);
        subCommands.publish(channel, rules);
        return subCommands.exec();
    }

    /**
     * Build Redis client fromm {@code RedisConnectionConfig}.
     *
     * @return a new {@link RedisClient}
     */
    private RedisClient getRedisClient(RedisConnectionConfig connectionConfig) {
        if (connectionConfig.getRedisSentinels().size() == 0) {
            RecordLog.info("[RedisDataSource] Creating stand-alone mode Redis client");
            return getRedisStandaloneClient(connectionConfig);
        } else {
            RecordLog.info("[RedisDataSource] Creating Redis Sentinel mode Redis client");
            return getRedisSentinelClient(connectionConfig);
        }
    }

    private RedisClient getRedisStandaloneClient(RedisConnectionConfig connectionConfig) {
        char[] password = connectionConfig.getPassword();
        String clientName = connectionConfig.getClientName();
        RedisURI.Builder redisUriBuilder = RedisURI.builder();
        redisUriBuilder.withHost(connectionConfig.getHost())
                .withPort(connectionConfig.getPort())
                .withDatabase(connectionConfig.getDatabase())
                .withTimeout(Duration.ofMillis(connectionConfig.getTimeout()));
        if (password != null) {
            redisUriBuilder.withPassword(connectionConfig.getPassword());
        }
        if (StringUtil.isNotEmpty(connectionConfig.getClientName())) {
            redisUriBuilder.withClientName(clientName);
        }
        return RedisClient.create(redisUriBuilder.build());
    }

    private RedisClient getRedisSentinelClient(RedisConnectionConfig connectionConfig) {
        char[] password = connectionConfig.getPassword();
        String clientName = connectionConfig.getClientName();
        RedisURI.Builder sentinelRedisUriBuilder = RedisURI.builder();
        for (RedisConnectionConfig config : connectionConfig.getRedisSentinels()) {
            sentinelRedisUriBuilder.withSentinel(config.getHost(), config.getPort());
        }
        if (password != null) {
            sentinelRedisUriBuilder.withPassword(connectionConfig.getPassword());
        }
        if (StringUtil.isNotEmpty(connectionConfig.getClientName())) {
            sentinelRedisUriBuilder.withClientName(clientName);
        }
        sentinelRedisUriBuilder.withSentinelMasterId(connectionConfig.getRedisSentinelMasterId())
                .withTimeout(connectionConfig.getTimeout(), TimeUnit.MILLISECONDS);
        return RedisClient.create(sentinelRedisUriBuilder.build());
    }
}
